package com.magi.core;

import com.magi.config.BuildingConfig;
import com.magi.config.IDatabaseConfig;
import com.magi.constant.DatabaseProducts;
import com.magi.factory.ITableMetaDateFactory;
import com.magi.factory.MysqlTableMetaFactory;

import java.net.URL;
import java.net.URLClassLoader;
import java.sql.*;
import java.util.List;

public class DatabaseOperator implements IDatabaseOperator{
    private static IDatabaseOperator instance;
    private final IDatabaseConfig config;
    private final ITableMetaDateFactory tableMateDateFactory;

    {
        this.loadDatabaseDrivers();
    }

    public DatabaseOperator(IDatabaseConfig config) throws SQLException{
        this.config = config;
        Connection conn = config.getUsername() == null ? this.getConnection() : this.getConnection(config.getUsername(), config.getPassword());

        DatabaseMetaData metaData = conn.getMetaData();
        String productName = metaData.getDatabaseProductName();
        if (DatabaseProducts.MYSQL.equals(productName)) {
            String databaseName = this.getDatabaseNameMysql(conn);
            if (databaseName == null) { throw new RuntimeException("Not connect to a valid database."); }
            tableMateDateFactory = new MysqlTableMetaFactory(databaseName, metaData);
        } else {
            throw new RuntimeException("not support for database product '" + productName + "'");
        }
    }



    private String getDatabaseNameMysql(Connection conn) throws SQLException {
        try(Statement stmt = conn.createStatement()) {
            ResultSet resultSet = stmt.executeQuery("select database() AS databaseName");
            if (resultSet.next()) {
                return resultSet.getString("databaseName");
            }
        }
        return null;
    }

    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(this.config.getDatabaseURL());
    }

    private Connection getConnection(String n, String p) throws SQLException {
        return DriverManager.getConnection(this.config.getDatabaseURL(), n, p);
    }

    private void loadDatabaseDrivers(){
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
           throw new RuntimeException(e);
        }
    }

    private void loadDriverJar(String connectorName, String driverRef) throws SQLException {
        URL driverURL = ClassLoader.getSystemResource("connectors/" + connectorName);
        URLClassLoader classLoader = new URLClassLoader(new URL[]{driverURL});

        Class<?> clazz;
        try {
            clazz = classLoader.loadClass(driverRef);

            System.out.println(clazz.getName());

            DriverManager.registerDriver((Driver) clazz.newInstance());
            DriverManager.getDrivers();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public IDatabaseOperator getInstance(IDatabaseConfig config) throws SQLException {
        if (instance == null) { instance = new DatabaseOperator(config); }
        return instance;
    }

    @Override
    public TableMetaData getTableMateData(String tableName) throws SQLException {
        return tableMateDateFactory.getMateDate(tableName);
    }

    @Override
    public List<TableMetaData> getAllTableMateData() throws SQLException {
        return tableMateDateFactory.getAllMateDate();
    }

    public static void main(String[] args) throws SQLException {
        BuildingConfig config = new BuildingConfig();
        config.setUsername("mdy");
        config.setPassword("123456");
        config.setDatabaseURL("jdbc:mysql://www.hdm6601.top/fresh?useSSL=false");
        IDatabaseOperator operator = new DatabaseOperator(config);
        List<TableMetaData> metaDataList = operator.getAllTableMateData();
        for (TableMetaData metaData : metaDataList) {
            System.out.println(metaData.getTableName());
        }
    }
}
